﻿using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Net;
using System.Reactive.Linq;
using System.Text.RegularExpressions;
using HtmlAgilityPack;
using Wulong.Phone.Utils.HttpHelpers;

namespace Wulong.Phone.Qiushi.Models
{
    public static class QiushiMapper
    {
        private const string BaseUrl = "http://www.qiushibaike.com";

        public static IObservable<QiushiComment> GetObservableComment(int id)
        {
            const string commentsQuery = BaseUrl + "/wap2_comments.php?id={0}";
            return Observable.Return((HttpWebRequest) WebRequest.Create(new Uri(string.Format(commentsQuery, id))))
                .SelectMany(request => Observable.FromAsyncPattern<WebResponse>(request.BeginGetResponse,
                                                                                request.EndGetResponse)())
                .SelectMany(response => GetComments(response.GetResponseStream()).ToObservable())
                ;
        }

        private static IEnumerable<QiushiComment> GetComments(Stream stream)
        {
            var rComments = new Regex("&nbsp;(.*?)&nbsp;(.*?)<br/></div>", RegexOptions.IgnoreCase & RegexOptions.Multiline);
            var htmlBody = new StreamReader(stream).ReadToEnd();
            var matchCollection = rComments.Matches(htmlBody);
            var enumerator = matchCollection.GetEnumerator();
            try
            {
                var floor = 0;
                while (enumerator.MoveNext())
                {
                    var match = (Match)enumerator.Current;
                    if(match==null)
                        break;
                    var matchedBlock = match.Groups[1].Value;
                    var comment = new QiushiComment
                                      {
                                          Author = matchedBlock.StartsWith("<a")
                                                       ? Regex.Match(matchedBlock, ">(.*?)</a>").Groups[1].
                                                             Value
                                                       : matchedBlock,
                                          Body = TrimHtml(match.Groups[2].Value),
                                          Floor = (++floor).ToString() + "L "
                                      };
                    yield return comment;
                }
            }
            finally
            {
                var disposable = enumerator as IDisposable;
                if (disposable != null)
                    disposable.Dispose();
            }
        }

        public static IObservable<QiushiItem> GetObservableItems(string tab = "l", int itemsPerPage = 20,
                                                                 int offset = 0)
        {
            const string naviUrl = BaseUrl + "/wap2_index.php?f={0}&l={1}&s={2}";
            return
                GetObservableItems(
                    new Uri(String.Format(naviUrl, tab, itemsPerPage, offset)));
        }

        public static IObservable<QiushiItem> GetObservableItems(Uri uri)
        {
            return Observable.Return((HttpWebRequest) WebRequest.Create(uri))
                .SelectMany(request => Observable.FromAsyncPattern<WebResponse>(
                    request.BeginGetResponse, request.EndGetResponse)())
                .SelectMany(response => GetItem(response.GetResponseStream()).ToObservable())
                ;
        }

        private static IEnumerable<QiushiItem> GetItem(Stream stream)
        {
            var rId = new Regex("new2/article/(\\d+)\"><strong>(\\d+)</strong>", RegexOptions.Singleline);
            var rContent = new Regex("(.*?)(<a|<p)", RegexOptions.IgnoreCase & RegexOptions.Multiline);
            var rDown = new Regex("alt=\"囧\"/>(\\d+|-\\d+)", RegexOptions.Singleline);
            var rImage = new Regex("<img.*?src=\"(.*?pictures.*?)\"", RegexOptions.Singleline);
            var rTags = new Regex("rel=\"tag\">(.*?)</a>", RegexOptions.Singleline);
            var rUp = new Regex("alt=\"支持\"/>(\\d)+", RegexOptions.Singleline);
            var rItem = new Regex("<div class=\"qiushi\">(.*?)评论</a></p>", RegexOptions.IgnoreCase & RegexOptions.Multiline);
            var htmlBody = new StreamReader(stream).ReadToEnd();
            var matchCollection = rItem.Matches(htmlBody);
            var enumerator = matchCollection.GetEnumerator();
            try
            {
                while (enumerator.MoveNext())
                {
                    var match = (Match)enumerator.Current;
                    if (match == null) continue;
                    var item = new QiushiItem();
                    var itemMatch = match.Groups[1].Value;
                    item.Id = int.Parse(rId.Match(itemMatch).Groups[1].Value);
                    item.Content = TrimHtml(rContent.Match(itemMatch).Groups[1].Value);
                    item.ImageUrl = rImage.Match(itemMatch).Groups[1].Value.ToLower();
                    item.Tops = int.Parse(rUp.Match(itemMatch).Groups[1].Value);
                    item.Flops = Math.Abs(int.Parse(rDown.Match(itemMatch).Groups[1].Value));
                    item.CommentsCount = int.Parse(rId.Match(itemMatch).Groups[2].Value);
                    var tagsCollection = rTags.Matches(itemMatch);
                    var tagEumerator = tagsCollection.GetEnumerator();
                    while (tagEumerator.MoveNext())
                    {
                        var tag = (Match)tagEumerator.Current;
                        if (tag != null) item.Tags += tag.Groups[1].Value;
                    }
                    yield return item;
                }
            }
            finally
            {
                var disposable = enumerator as IDisposable;
                if (disposable != null)
                    disposable.Dispose();
            }
        }

        private static string TrimHtml(string strHtml)
        {
            return HttpUtility.HtmlDecode(strHtml.Replace("<br>", "\n").Replace("<br/>", "\n")).Trim(); ;
        }

        public static IObservable<string> PostQiushiArticle(Dictionary<string, object> postParameters)
        {
            const string postUrl = "http://posttestserver.com/post.php";//BaseUrl + "/new2/add";
            const string userAgent =
                "Mozilla/5.0 (compatible; MSIE 9.0; Windows NT 6.1; Trident/5.0)";
            return MultipartFormDataPost(postUrl, userAgent, postParameters);
        }

        public static IObservable<string> MultipartFormDataPost(string postUrl, string userAgent, Dictionary<string, object> postParameters)
        {
            string formDataBoundary = "---------------------------" + DateTime.Now.Ticks.ToString("x");//"---------------------------7dbe4045032c"; //"-----------------------------28947758029299";
                                             
            string contentType = "multipart/form-data; boundary=" + formDataBoundary;

            byte[] formData = PostHelper.GetMultipartFormData(postParameters, formDataBoundary);

            return PostForm(postUrl, userAgent, contentType, formData);
        }


        private static IObservable<string> PostForm(string postUrl, string userAgent, string contentType, byte[] formData)
        {
            var request = (HttpWebRequest)WebRequest.Create(new Uri(postUrl));//test with "http://posttestserver.com/post.php"
            request.Method = "POST";
            request.ContentType = contentType;
            request.UserAgent = userAgent;
            request.CookieContainer = new CookieContainer();

            var fetchRequestStream = Observable.FromAsyncPattern<Stream>(request.BeginGetRequestStream,
                                                                         request.EndGetRequestStream);
            var fetchResponse = Observable.FromAsyncPattern<WebResponse>(request.BeginGetResponse,
                                                                         request.EndGetResponse);
            return fetchRequestStream()
                .SelectMany(stream =>
                            {
                                using (var writer = new StreamWriter(stream))
                                    writer.Write(formData);
                                
                                return fetchResponse();
                            })
                .Select(result =>
                            {
                                var response = (HttpWebResponse)result;
                                return GetPostResponse(response.GetResponseStream());
                            })
                .Catch<string, Exception>(e=> Observable.Return(e.Message))
                .ObserveOnDispatcher();
        }

        private static string GetPostResponse(Stream stream)
        {
            var doc = new HtmlDocument();
            doc.Load(stream);
            var nodes =from n in doc.DocumentNode.Descendants("div")
                        where
                            n.HasAttributes && n.Attributes[0].Value.Equals("tagcloud")
                        select n.InnerText;
            return nodes.FirstOrDefault();
        }
    }
}